/*
 *  Multi2Sim
 *  Copyright (C) 2012  Rafael Ubal (ubal@ece.neu.edu)
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <assert.h>
#include <elf.h>
#include <stdio.h>
#include <string.h>

#include "../include/cuda.h"
#include "../include/cuda_runtime_api.h"
#include "api.h"
#include "debug.h"
#include "device.h"
#include "elf-format.h"
#include "function-arg.h"
#include "function.h"
#include "list.h"
#include "mhandle.h"


/*
 * Global Variables
 */

/* Execution stack */
struct list_t *execution_stack;

/* CUDA function list */
struct list_t *runtime_func_list;

/* Error code for last runtime function call */
cudaError_t cuda_rt_last_error;

/* Error */
char *cuda_rt_err_not_impl = "\tMulti2Sim provides partial support for CUDA\n"
"\truntime library. To request the implementation of a certain functionality,\n"
"\tplease email development@multi2sim.org.\n";

char *cuda_rt_err_native = "\tYou are trying to run natively an application\n"
"\tusing the Multi2Sim CUDA runtime/driver library implementation\n"
"\t('libm2s-cuda'). Please run this program on top of Multi2Sim.\n";

char *cuda_rt_err_param_note = "\tThis error message is generated by the\n"
"\tMulti2Sim CUDA runtime library linked with your CUDA host program. While\n"
"\ta complete CUDA implementation would return an error code to your\n"
"\tapplication, the Multi2Sim CUDA runtime just makes your program fail with\n"
"\tan error message.\n";
#define __CUDART_NOT_IMPL__  warning("%s: not implemented.\n%s", __FUNCTION__, \
		cuda_rt_err_not_impl)




/*
 * Private Functions
 */

static unsigned char get_uchar(const unsigned long long int *dev_func_bin_sec,
		int index)
{
	return (unsigned char)
		((dev_func_bin_sec[index / 8] >> (index % 8 * 8)) & 0xff);
}

static unsigned int get_uint(const unsigned long long int *dev_func_bin_sec, 
		int start_index)
{
	return (unsigned int)
		((unsigned int)get_uchar(dev_func_bin_sec, 
			start_index) |
		 ((unsigned int)get_uchar(dev_func_bin_sec, 
			 start_index + 1)) << 8 |
		 ((unsigned int)get_uchar(dev_func_bin_sec, 
			 start_index + 2)) << 16 |
		 ((unsigned int)get_uchar(dev_func_bin_sec, 
			 start_index + 3)) << 24);
}




/*
 * CUDA Runtime Internal Functions
 */

void **__cudaRegisterFatBinary(void *fatCubin)
{
	void **fatCubinHandle;

	cuInit(0);

	cuda_debug("CUDA runtime internal function '%s'",
			__FUNCTION__);

	fatCubinHandle = xcalloc(1, sizeof(void *));
	*fatCubinHandle = fatCubin;

	cuda_debug("\t(runtime) out: return");

	return fatCubinHandle;
}

void __cudaUnregisterFatBinary(void **fatCubinHandle)
{
	cuda_debug("CUDA runtime internal function '%s'",
			__FUNCTION__);

	if (fatCubinHandle != NULL)
		free(fatCubinHandle);

	cuda_debug("\t(runtime) out: return");
}

void __cudaRegisterVar(void **fatCubinHandle,
		char *hostVar,
		char *deviceAddress,
		const char *deviceName,
		int ext,
		int size,
		int constant,
		int global)
{
	__CUDART_NOT_IMPL__;
}

void __cudaRegisterTexture(void **fatCubinHandle,
		const struct textureReference *hostVar,
		const void **deviceAddress,
		const char *deviceName,
		int dim,
		int norm,
		int ext)
{
	__CUDART_NOT_IMPL__;
}

void __cudaRegisterSurface(void **fatCubinHandle,
		const struct surfaceReference *hostVar,
		const void **deviceAddress,
		const char *deviceName,
		int dim,
		int ext)
{
	__CUDART_NOT_IMPL__;
}

void __cudaRegisterFunction(void **fatCubinHandle,
		const char *hostFun,
		char *deviceFun,
		const char *deviceName,
		int thread_limit,
		uint3 *tid,
		uint3 *bid,
		dim3 *bDim,
		dim3 *gDim,
		int *wSize)
{
	char *cubin_path;
	char *binary_filename;
	char deviceFun_arr[1024];

	const unsigned long long int *dev_func_bin_sec;
	int dev_func_bin_sec_size;
	int elf_head;
	struct elf_file_t *dev_func_bin;
	FILE *dev_func_bin_f;
	char abi_version;

	CUmodule module;
	CUfunction function;

	void *ptr;
	int size;
	int i;

	cuda_debug("CUDA runtime internal function '%s'", __FUNCTION__);
	cuda_debug("\t(runtime) in: hostFun = %p", hostFun);
	cuda_debug("\t(runtime) in: deviceFun = %s", deviceFun);
	cuda_debug("\t(runtime) in: deviceName = %s", deviceName);
	cuda_debug("\t(runtime) in: thread_limit = %d", thread_limit);
	cuda_debug("\t(runtime) in: tid = %p", tid);
	cuda_debug("\t(runtime) in: bid = %p", bid);
	cuda_debug("\t(runtime) in: bDim = %p", bDim);
	cuda_debug("\t(runtime) in: gDim = %p", gDim);
	cuda_debug("\t(runtime) in: wSize = %p", wSize);

	/* User can set an environment variable 'M2S_OPENCL_BINARY' to make the
	 * runtime load that specific pre-compiled binary. */
	cubin_path = getenv("M2S_CUDA_BINARY");
	if (cubin_path && !strchr(cubin_path, '/'))
		fatal("%s: Please set M2S_CUDA_BINARY to the path of the cubin",
				__FUNCTION__);

	/* Get device function identifier. If M2S_CUDA_BINARY is set, we assume
	 * the binary filename (excluding .cubin) is the device function
	 * identifier */
	if (cubin_path)
	{
		binary_filename = strrchr(cubin_path, '/') + 1;
		assert(!strncmp(binary_filename + strlen(binary_filename) - 6,
					".cubin", 6));
		assert(strlen(binary_filename) < sizeof deviceFun_arr);
		strncpy(deviceFun_arr, binary_filename, 
				strlen(binary_filename) - 6);
		deviceFun_arr[strlen(binary_filename) - 6] = '\0';
		deviceFun = deviceFun_arr;
	}

	/* Get device function binary */
	if (!cubin_path)
	{
		/* Get the section containing device function binary */
		dev_func_bin_sec = 
			(*(struct {int m; int v; 
			   const unsigned long long int *d; char *f;} **)
			 fatCubinHandle)->d;
		dev_func_bin_sec_size = 
			*(((unsigned long long int *)dev_func_bin_sec) + 1) +
			16;

		/* Look for ELF head */
		elf_head = -1;
		for (i = 2; i < dev_func_bin_sec_size; ++i)
		{
			if (get_uint(dev_func_bin_sec, i) == 0x464c457f)
			{
				elf_head = i;
				break;
			}
		}
		assert(elf_head != -1);

		/* Get device function binary 
		 * Notice that this binary may be larger than the actual binary
		 * since we cannot determine the end of the binary. */
		ptr = (void *)(dev_func_bin_sec + 
				elf_head / sizeof(unsigned long long int)); 
		size = 16 + dev_func_bin_sec_size - elf_head;
		dev_func_bin = elf_file_create_from_buffer(ptr, size, NULL);

		/* Save device function binary in a tmp file for later use in
		 * CUDA driver */
		cubin_path = tmpnam(NULL);
		dev_func_bin_f = fopen(cubin_path, "wb");
		elf_buffer_dump(&(dev_func_bin->buffer), dev_func_bin_f);
		fclose(dev_func_bin_f);
	}
	else
		dev_func_bin = elf_file_create_from_path(cubin_path);

	/* Check for Fermi binary. Support for Fermi only for now. */
	elf_buffer_seek(&(dev_func_bin->buffer), 8);
	elf_buffer_read(&(dev_func_bin->buffer), &abi_version, 1);
	if (abi_version < 4 || abi_version > 6)
		fatal("%s: The cubin has a unrecognized ABI version (0x%x). "
				"Multi2Sim CUDA library is currently "
				"compatible with Fermi binary only.",
				__FUNCTION__, abi_version);

	/* Load module */
	cuModuleLoad(&module, cubin_path);

	/* Get device function */
	cuModuleGetFunction(&function, module, deviceFun);

	/* Save host function pointer, which is used as an index to find the
	 * function */
	function->host_func_ptr = (unsigned int)hostFun;

	/* Create list */
	if (!runtime_func_list)
		runtime_func_list = list_create();

	/* Add function to stack */
	list_add(runtime_func_list, function);
//printf("%p %p\n", list_get(runtime_func_list, 0), *(CUfunction *)list_get(runtime_func_list, 0));

	/* Free */
	elf_file_free(dev_func_bin);

	cuda_debug("\t(runtime) out: return");
}




/*
 * CUDA Runtime API
 */

cudaError_t cudaDeviceReset(void)
{
	cuda_debug("CUDA runtime API '%s'", __FUNCTION__);

	/* This function is not implemented for now since resources are freed in
	 * cuda_done() */

	cuda_rt_last_error = cudaSuccess;

	cuda_debug("\t(runtime) out: return = %d", cudaSuccess);

	return cudaSuccess;
}

cudaError_t cudaDeviceSynchronize(void)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaDeviceSetLimit(enum cudaLimit limit, size_t value)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaDeviceGetLimit(size_t *pValue, enum cudaLimit limit)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaDeviceGetCacheConfig(enum cudaFuncCache *pCacheConfig)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaDeviceSetCacheConfig(enum cudaFuncCache cacheConfig)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaThreadExit(void)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaThreadSynchronize(void)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaThreadSetLimit(enum cudaLimit limit, size_t value)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaThreadGetLimit(size_t *pValue, enum cudaLimit limit)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaThreadGetCacheConfig(enum cudaFuncCache *pCacheConfig)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaThreadSetCacheConfig(enum cudaFuncCache cacheConfig)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaGetLastError(void)
{
	cudaError_t cuda_rt_last_error_ret;

	cuda_debug("CUDA runtime API '%s'", __FUNCTION__);

	cuda_rt_last_error_ret = cuda_rt_last_error;

	/* Reset */
	cuda_rt_last_error = cudaSuccess;

	cuda_debug("\t(runtime) out: return = %d",
			cuda_rt_last_error_ret);

	return cuda_rt_last_error_ret;
}

cudaError_t cudaPeekAtLastError(void)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}


const char* cudaGetErrorString(cudaError_t error)
{
	__CUDART_NOT_IMPL__;
	return NULL;
}

cudaError_t cudaGetDeviceCount(int *count)
{
	cuda_debug("CUDA runtime API '%s'", __FUNCTION__);

	/* 0 for Fermi, 1 for Kepler */
	*count = 2;	

	cuda_debug("\t(runtime) out: count = [%p] %d", 
			count, *count);

	cuda_rt_last_error = cudaSuccess;

	cuda_debug("\t(runtime) out: return = %d", cudaSuccess);

	return cudaSuccess;
}

cudaError_t cudaGetDeviceProperties(struct cudaDeviceProp *prop_ptr, int device)
{
	struct cuda_device_t *dev;
	unsigned int free;
	unsigned int total;

	cuda_debug("CUDA runtime API '%s'", __FUNCTION__);
	cuda_debug("\t(runtime) in: device = %d", device);

	/* Check for valid properties pointer */
	if (!prop_ptr)
		fatal("%s: invalid value for 'prop_ptr'.\n%s",
				__FUNCTION__, cuda_rt_err_param_note);

	/* Check for valid device. For now, we just check that the device is
	 * 0 (default), but later we will check among an array of possible valid
	 * devices. */
	if (device != 0)
		fatal("%s: invalid device (%d).\n%s", __FUNCTION__,
				device, cuda_rt_err_param_note);

	/* Get device */
	dev = (struct cuda_device_t *)list_get(device_list, device);

	/* Get device properties */
	strncpy(prop_ptr->name, dev->name, sizeof prop_ptr->name);
	cuMemGetInfo(&free, &total);
	prop_ptr->totalGlobalMem = total;
	prop_ptr->sharedMemPerBlock =
		dev->attributes[CU_DEVICE_ATTRIBUTE_MAX_SHARED_MEMORY_PER_BLOCK];
	prop_ptr->regsPerBlock =
		dev->attributes[CU_DEVICE_ATTRIBUTE_MAX_REGISTERS_PER_BLOCK];
	prop_ptr->warpSize = dev->attributes[CU_DEVICE_ATTRIBUTE_WARP_SIZE];
	prop_ptr->memPitch = dev->attributes[CU_DEVICE_ATTRIBUTE_MAX_PITCH];
	prop_ptr->maxThreadsPerBlock =
		dev->attributes[CU_DEVICE_ATTRIBUTE_MAX_THREADS_PER_BLOCK];
	prop_ptr->maxThreadsDim[0] =
		dev->attributes[CU_DEVICE_ATTRIBUTE_MAX_BLOCK_DIM_X];
	prop_ptr->maxThreadsDim[1] =
		dev->attributes[CU_DEVICE_ATTRIBUTE_MAX_BLOCK_DIM_Y];
	prop_ptr->maxThreadsDim[2] =
		dev->attributes[CU_DEVICE_ATTRIBUTE_MAX_BLOCK_DIM_Z];
	prop_ptr->maxGridSize[0] =
		dev->attributes[CU_DEVICE_ATTRIBUTE_MAX_GRID_DIM_X];
	prop_ptr->maxGridSize[1] =
		dev->attributes[CU_DEVICE_ATTRIBUTE_MAX_GRID_DIM_Y];
	prop_ptr->maxGridSize[2] =
		dev->attributes[CU_DEVICE_ATTRIBUTE_MAX_GRID_DIM_Z];
	prop_ptr->clockRate = dev->attributes[CU_DEVICE_ATTRIBUTE_CLOCK_RATE];
	prop_ptr->totalConstMem =
		dev->attributes[CU_DEVICE_ATTRIBUTE_TOTAL_CONSTANT_MEMORY];
	prop_ptr->major =
		dev->attributes[CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR];
	prop_ptr->minor =
		dev->attributes[CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR];
	prop_ptr->textureAlignment =
		dev->attributes[CU_DEVICE_ATTRIBUTE_TEXTURE_ALIGNMENT];
	prop_ptr->texturePitchAlignment =
		dev->attributes[CU_DEVICE_ATTRIBUTE_TEXTURE_PITCH_ALIGNMENT];
	prop_ptr->deviceOverlap =
		dev->attributes[CU_DEVICE_ATTRIBUTE_GPU_OVERLAP];
	prop_ptr->multiProcessorCount =
		dev->attributes[CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT];
	prop_ptr->kernelExecTimeoutEnabled =
		dev->attributes[CU_DEVICE_ATTRIBUTE_KERNEL_EXEC_TIMEOUT];
	prop_ptr->integrated = dev->attributes[CU_DEVICE_ATTRIBUTE_INTEGRATED];
	prop_ptr->canMapHostMemory =
		dev->attributes[CU_DEVICE_ATTRIBUTE_CAN_MAP_HOST_MEMORY];
	prop_ptr->computeMode =
		dev->attributes[CU_DEVICE_ATTRIBUTE_COMPUTE_MODE];
	prop_ptr->maxTexture1D =
		dev->attributes[CU_DEVICE_ATTRIBUTE_MAXIMUM_TEXTURE1D_WIDTH];
	prop_ptr->maxTexture1DMipmap =
		dev->attributes[CU_DEVICE_ATTRIBUTE_MAXIMUM_TEXTURE1D_MIPMAPPED_WIDTH];
	prop_ptr->maxTexture1DLinear =
		dev->attributes[CU_DEVICE_ATTRIBUTE_MAXIMUM_TEXTURE1D_LINEAR_WIDTH];
	prop_ptr->maxTexture2D[0] =
		dev->attributes[CU_DEVICE_ATTRIBUTE_MAXIMUM_TEXTURE2D_WIDTH];
	prop_ptr->maxTexture2D[1] =
		dev->attributes[CU_DEVICE_ATTRIBUTE_MAXIMUM_TEXTURE2D_HEIGHT];
	prop_ptr->maxTexture2DMipmap[0] =
		dev->attributes[CU_DEVICE_ATTRIBUTE_MAXIMUM_TEXTURE2D_MIPMAPPED_WIDTH];
	prop_ptr->maxTexture2DMipmap[1] =
		dev->attributes[CU_DEVICE_ATTRIBUTE_MAXIMUM_TEXTURE2D_MIPMAPPED_HEIGHT];
	prop_ptr->maxTexture2DLinear[0] =
		dev->attributes[CU_DEVICE_ATTRIBUTE_MAXIMUM_TEXTURE2D_LINEAR_WIDTH];
	prop_ptr->maxTexture2DLinear[1] =
		dev->attributes[CU_DEVICE_ATTRIBUTE_MAXIMUM_TEXTURE2D_LINEAR_HEIGHT];
	prop_ptr->maxTexture2DLinear[2] =
		dev->attributes[CU_DEVICE_ATTRIBUTE_MAXIMUM_TEXTURE2D_LINEAR_PITCH];
	prop_ptr->maxTexture2DGather[0] =
		dev->attributes[CU_DEVICE_ATTRIBUTE_MAXIMUM_TEXTURE2D_GATHER_WIDTH];
	prop_ptr->maxTexture2DGather[1] =
		dev->attributes[CU_DEVICE_ATTRIBUTE_MAXIMUM_TEXTURE2D_GATHER_HEIGHT];
	prop_ptr->maxTexture3D[0] =
		dev->attributes[CU_DEVICE_ATTRIBUTE_MAXIMUM_TEXTURE3D_WIDTH];
	prop_ptr->maxTexture3D[1] =
		dev->attributes[CU_DEVICE_ATTRIBUTE_MAXIMUM_TEXTURE3D_HEIGHT];
	prop_ptr->maxTexture3D[2] =
		dev->attributes[CU_DEVICE_ATTRIBUTE_MAXIMUM_TEXTURE3D_DEPTH];
	prop_ptr->maxTextureCubemap =
		dev->attributes[CU_DEVICE_ATTRIBUTE_MAXIMUM_TEXTURECUBEMAP_WIDTH];
	prop_ptr->maxTexture1DLayered[0] =
		dev->attributes[CU_DEVICE_ATTRIBUTE_MAXIMUM_TEXTURE1D_LAYERED_WIDTH];
	prop_ptr->maxTexture1DLayered[1] =
		dev->attributes[CU_DEVICE_ATTRIBUTE_MAXIMUM_TEXTURE1D_LAYERED_LAYERS];
	prop_ptr->maxTexture2DLayered[0] =
		dev->attributes[CU_DEVICE_ATTRIBUTE_MAXIMUM_TEXTURE2D_LAYERED_WIDTH];
	prop_ptr->maxTexture2DLayered[1] =
		dev->attributes[CU_DEVICE_ATTRIBUTE_MAXIMUM_TEXTURE2D_LAYERED_HEIGHT];
	prop_ptr->maxTexture2DLayered[2] =
		dev->attributes[CU_DEVICE_ATTRIBUTE_MAXIMUM_TEXTURE2D_LAYERED_LAYERS];
	prop_ptr->maxTextureCubemapLayered[0] =
		dev->attributes[CU_DEVICE_ATTRIBUTE_MAXIMUM_TEXTURECUBEMAP_LAYERED_WIDTH];
	prop_ptr->maxTextureCubemapLayered[1] =
		dev->attributes[CU_DEVICE_ATTRIBUTE_MAXIMUM_TEXTURECUBEMAP_LAYERED_LAYERS];
	prop_ptr->maxSurface1D =
		dev->attributes[CU_DEVICE_ATTRIBUTE_MAXIMUM_SURFACE1D_WIDTH];
	prop_ptr->maxSurface2D[0] =
		dev->attributes[CU_DEVICE_ATTRIBUTE_MAXIMUM_SURFACE2D_WIDTH];
	prop_ptr->maxSurface2D[1] =
		dev->attributes[CU_DEVICE_ATTRIBUTE_MAXIMUM_SURFACE2D_HEIGHT];
	prop_ptr->maxSurface3D[0] =
		dev->attributes[CU_DEVICE_ATTRIBUTE_MAXIMUM_SURFACE3D_WIDTH];
	prop_ptr->maxSurface3D[1] =
		dev->attributes[CU_DEVICE_ATTRIBUTE_MAXIMUM_SURFACE3D_HEIGHT];
	prop_ptr->maxSurface3D[2] =
		dev->attributes[CU_DEVICE_ATTRIBUTE_MAXIMUM_SURFACE3D_DEPTH];
	prop_ptr->maxSurface1DLayered[0] =
		dev->attributes[CU_DEVICE_ATTRIBUTE_MAXIMUM_SURFACE1D_LAYERED_WIDTH];
	prop_ptr->maxSurface1DLayered[1] =
		dev->attributes[CU_DEVICE_ATTRIBUTE_MAXIMUM_SURFACE1D_LAYERED_LAYERS];
	prop_ptr->maxSurface2DLayered[0] =
		dev->attributes[CU_DEVICE_ATTRIBUTE_MAXIMUM_SURFACE2D_LAYERED_WIDTH];
	prop_ptr->maxSurface2DLayered[1] =
		dev->attributes[CU_DEVICE_ATTRIBUTE_MAXIMUM_SURFACE2D_LAYERED_HEIGHT];
	prop_ptr->maxSurface2DLayered[2] =
		dev->attributes[CU_DEVICE_ATTRIBUTE_MAXIMUM_SURFACE2D_LAYERED_LAYERS];
	prop_ptr->maxSurfaceCubemap =
		dev->attributes[CU_DEVICE_ATTRIBUTE_MAXIMUM_SURFACECUBEMAP_WIDTH];
	prop_ptr->maxSurfaceCubemapLayered[0] =
		dev->attributes[CU_DEVICE_ATTRIBUTE_MAXIMUM_SURFACECUBEMAP_LAYERED_WIDTH];
	prop_ptr->maxSurfaceCubemapLayered[1] =
		dev->attributes[CU_DEVICE_ATTRIBUTE_MAXIMUM_SURFACECUBEMAP_LAYERED_LAYERS];
	prop_ptr->surfaceAlignment =
		dev->attributes[CU_DEVICE_ATTRIBUTE_SURFACE_ALIGNMENT];
	prop_ptr->concurrentKernels =
		dev->attributes[CU_DEVICE_ATTRIBUTE_CONCURRENT_KERNELS];
	prop_ptr->ECCEnabled = dev->attributes[CU_DEVICE_ATTRIBUTE_ECC_ENABLED];
	prop_ptr->pciBusID = dev->attributes[CU_DEVICE_ATTRIBUTE_PCI_BUS_ID];
	prop_ptr->pciDeviceID =
		dev->attributes[CU_DEVICE_ATTRIBUTE_PCI_DEVICE_ID];
	prop_ptr->pciDomainID =
		dev->attributes[CU_DEVICE_ATTRIBUTE_PCI_DOMAIN_ID];
	prop_ptr->tccDriver = dev->attributes[CU_DEVICE_ATTRIBUTE_TCC_DRIVER];
	prop_ptr->asyncEngineCount =
		dev->attributes[CU_DEVICE_ATTRIBUTE_ASYNC_ENGINE_COUNT];
	prop_ptr->unifiedAddressing =
		dev->attributes[CU_DEVICE_ATTRIBUTE_UNIFIED_ADDRESSING];
	prop_ptr->memoryClockRate =
		dev->attributes[CU_DEVICE_ATTRIBUTE_MEMORY_CLOCK_RATE];
	prop_ptr->memoryBusWidth =
		dev->attributes[CU_DEVICE_ATTRIBUTE_GLOBAL_MEMORY_BUS_WIDTH];
	prop_ptr->l2CacheSize =
		dev->attributes[CU_DEVICE_ATTRIBUTE_L2_CACHE_SIZE];
	prop_ptr->maxThreadsPerMultiProcessor =
		dev->attributes[CU_DEVICE_ATTRIBUTE_MAX_THREADS_PER_MULTIPROCESSOR];

	cuda_debug("\t(runtime) out: prop_ptr = %p", prop_ptr);

	cuda_rt_last_error = cudaSuccess;

	cuda_debug("\t(runtime) out: return = %d", cudaSuccess);

	return cudaSuccess;
}

cudaError_t cudaChooseDevice(int *device, const struct cudaDeviceProp *prop)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaSetDevice(int device)
{
	//Sets device as the current device for the calling host thread
	return cudaSuccess;
}

cudaError_t cudaGetDevice(int *device_ptr)
{
	/* Check valid device pointer */
	if (!device_ptr)
		fatal("%s: invalid device pointer.\n%s", __FUNCTION__,
				cuda_rt_err_param_note);

	/* We allow temporarily for only the Fermi device. We assigned a
	 * hardcoded device identifier equal to 0 (Fermi). This function will be
	 * extended later with support to Kepler. */
	*device_ptr = 0;

	return cudaSuccess;
}

cudaError_t cudaSetValidDevices(int *device_arr, int len)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaSetDeviceFlags( unsigned int flags )
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaStreamCreate(cudaStream_t *pStream)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaStreamDestroy(cudaStream_t stream)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaStreamWaitEvent(cudaStream_t stream, 
		cudaEvent_t event, 
		unsigned int flags)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaStreamSynchronize(cudaStream_t stream)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaStreamQuery(cudaStream_t stream)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaEventCreate(cudaEvent_t *event)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaEventCreateWithFlags(cudaEvent_t *event, unsigned int flags)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaEventRecord(cudaEvent_t event, cudaStream_t stream __dv(0))
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaEventQuery(cudaEvent_t event)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaEventSynchronize(cudaEvent_t event)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaEventDestroy(cudaEvent_t event)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaEventElapsedTime(float *ms, cudaEvent_t start, cudaEvent_t end)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaConfigureCall(dim3 gridDim, dim3 blockDim, 
		size_t sharedMem __dv(0), 
		cudaStream_t stream __dv(0))
{
	unsigned int *func_dim;

	cuda_debug("CUDA runtime API '%s'", __FUNCTION__);
	cuda_debug("\t(runtime) in: gridDim = %u %u %u", 
			gridDim.x, gridDim.y, gridDim.z);
	cuda_debug("\t(runtime) in: blockDim = %u %u %u", 
			blockDim.x, blockDim.y, blockDim.z);

	/* Create stack */
	if (!execution_stack)
		execution_stack = list_create();

	/* Create function dim */
	func_dim = (unsigned int *)xcalloc(1, sizeof gridDim + sizeof blockDim);
	func_dim[0] = gridDim.x;
	func_dim[1] = gridDim.y;
	func_dim[2] = gridDim.z;
	func_dim[3] = blockDim.x;
	func_dim[4] = blockDim.y;
	func_dim[5] = blockDim.z;

	/* Push to stack */
	list_push(execution_stack, func_dim);

	cuda_rt_last_error = cudaSuccess;

	cuda_debug("\t(runtime) out: return = %d", cudaSuccess);

	return cudaSuccess;
}

cudaError_t cudaSetupArgument(const void *arg, size_t size, size_t offset)
{
	struct cuda_function_arg_t *func_arg;

	cuda_debug("CUDA runtime API '%s'", __FUNCTION__);
	cuda_debug("\t(runtime) in: arg = %p", arg);
	cuda_debug("\t(runtime) in: size = %d", size);
	cuda_debug("\t(runtime) in: offset = %d", offset);

	/* Create function argument */
	func_arg = cuda_function_arg_create(arg, size, offset);

	/* Push to stack */
	list_push(execution_stack, func_arg);

	cuda_rt_last_error = cudaSuccess;

	cuda_debug("\t(runtime) out: return = %d", cudaSuccess);

	return cudaSuccess;
}

cudaError_t cudaFuncSetCacheConfig(const void *func, 
		enum cudaFuncCache cacheConfig)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaLaunch(const void *func)
{
	CUfunction function;

	unsigned int num_args;
	void ** arg_ptr_array;
	unsigned int *func_dim;

	int i;
	struct cuda_function_arg_t *arg;

	cuda_debug("CUDA runtime API '%s'", __FUNCTION__);
	cuda_debug("\t(runtime) in: func = %p", func);

	/* Get function */
	for (i = 0; i < list_count(runtime_func_list); i++)
	{
		function = (CUfunction)list_get(runtime_func_list, i);
		if (function->host_func_ptr == (unsigned int)func)
			break;
	}

	/* If no function found */
	if (i == list_count(runtime_func_list))
		fatal("%s: no function found", __FUNCTION__);

	/* Get arguments */
	num_args = list_count(execution_stack) - 1;
	arg_ptr_array = (void **)xcalloc(num_args, sizeof(void *));
	for (i = num_args - 1; i >= 0; i--)
	{
		arg = (struct cuda_function_arg_t *)list_pop(execution_stack);
		arg_ptr_array[i] = arg->ptr;
	}

	/* Get dim */
	func_dim = (unsigned int *)list_pop(execution_stack);

	assert(list_count(execution_stack) == 0);

	/* Launch kernel */
	cuLaunchKernel(function, func_dim[0], func_dim[1], func_dim[2], 
			func_dim[3], func_dim[4], func_dim[5], 
			0, NULL, arg_ptr_array, NULL);

	/* Free */
	if (func_dim)
		free(func_dim);
	if (arg_ptr_array)
		free(arg_ptr_array);

	cuda_rt_last_error = cudaSuccess;

	cuda_debug("\t(runtime) out: return = %d", cudaSuccess);

	return cudaSuccess;
}

cudaError_t cudaFuncGetAttributes(struct cudaFuncAttributes *attr, 
		const void *func)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaSetDoubleForDevice(double *d)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaSetDoubleForHost(double *d)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaMalloc(void **devPtr, size_t size)
{
	CUdeviceptr dptr;

	cuda_debug("CUDA runtime API '%s'", __FUNCTION__);
	cuda_debug("\t(runtime) in: size = %d", size);

	cuMemAlloc(&dptr, size);
	*(CUdeviceptr *)devPtr = dptr;

	cuda_debug("\t(runtime) out: devPtr = [%p] 0x%08x", 
			devPtr, *(CUdeviceptr *)devPtr);

	cuda_rt_last_error = cudaSuccess;

	cuda_debug("\t(runtime) out: return = %d", cudaSuccess);

	return cudaSuccess;
}

cudaError_t cudaMallocHost(void **ptr, size_t size)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaMallocPitch(void **devPtr, 
		size_t *pitch, 
		size_t width, 
		size_t height)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaMallocArray(struct cudaArray **array, 
		const struct cudaChannelFormatDesc *desc, 
		size_t width, 
		size_t height __dv(0), 
		unsigned int flags __dv(0))
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaFree(void *devPtr)
{
	cuda_debug("CUDA runtime API '%s'", __FUNCTION__);
	cuda_debug("\t(runtime) in: devPtr = %p", devPtr);

	cuMemFree((CUdeviceptr)devPtr);

	cuda_rt_last_error = cudaSuccess;

	cuda_debug("\t(runtime) out: return = %d", cudaSuccess);

	return cudaSuccess;
}

cudaError_t cudaFreeHost(void *ptr)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaFreeArray(struct cudaArray *array)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaHostAlloc(void **pHost, size_t size, unsigned int flags)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaHostRegister(void *ptr, size_t size, unsigned int flags)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaHostUnregister(void *ptr)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaHostGetDevicePointer(void **pDevice, 
		void *pHost, 
		unsigned int flags)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaHostGetFlags(unsigned int *pFlags, void *pHost)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaMalloc3D(struct cudaPitchedPtr* pitchedDevPtr, 
		struct cudaExtent extent)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaMalloc3DArray(struct cudaArray** array, 
		const struct cudaChannelFormatDesc* desc, 
		struct cudaExtent extent, 
		unsigned int flags __dv(0))
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaMemcpy3D(const struct cudaMemcpy3DParms *p)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaMemcpy3DPeer(const struct cudaMemcpy3DPeerParms *p)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaMemcpy3DAsync(const struct cudaMemcpy3DParms *p, 
		cudaStream_t stream __dv(0))
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaMemcpy3DPeerAsync(const struct cudaMemcpy3DPeerParms *p, 
		cudaStream_t stream __dv(0))
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaMemGetInfo(size_t *free, size_t *total)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaMemcpy(void *dst, 
		const void *src, 
		size_t count, 
		enum cudaMemcpyKind kind)
{
	cuda_debug("CUDA runtime API '%s'", __FUNCTION__);
	cuda_debug("\t(runtime) in: dst = %p", dst);
	cuda_debug("\t(runtime) in: src = %p", src);
	cuda_debug("\t(runtime) in: count = %d", count);
	cuda_debug("\t(runtime) in: kind = %d", kind);

	if (kind == cudaMemcpyHostToDevice)
		cuMemcpyHtoD((CUdeviceptr)dst, src, count);
	else if (kind == cudaMemcpyDeviceToHost)
		cuMemcpyDtoH(dst, (CUdeviceptr)src, count);
	/* FIXME: implement cudaMemcpyHostToHost, cudaMemcpyDeviceToDevice */

	cuda_rt_last_error = cudaSuccess;

	cuda_debug("\t(runtime) out: return = %d", cudaSuccess);

	return cudaSuccess;
}

cudaError_t cudaMemcpyPeer(void *dst, 
		int dstDevice, 
		const void *src, 
		int srcDevice, 
		size_t count)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaMemcpyToArray(struct cudaArray *dst, 
		size_t wOffset, 
		size_t hOffset, 
		const void *src, 
		size_t count, 
		enum cudaMemcpyKind kind)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaMemcpyFromArray(void *dst, 
		const struct cudaArray *src, 
		size_t wOffset, 
		size_t hOffset, 
		size_t count, 
		enum cudaMemcpyKind kind)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaMemcpyArrayToArray(struct cudaArray *dst, 
		size_t wOffsetDst, 
		size_t hOffsetDst, 
		const struct cudaArray *src, 
		size_t wOffsetSrc, 
		size_t hOffsetSrc, 
		size_t count, 
		enum cudaMemcpyKind kind __dv(cudaMemcpyDeviceToDevice))
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaMemcpy2D(void *dst, 
		size_t dpitch, 
		const void *src, 
		size_t spitch, 
		size_t width, 
		size_t height, 
		enum cudaMemcpyKind kind)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaMemcpy2DToArray(struct cudaArray *dst, 
		size_t wOffset, 
		size_t hOffset, 
		const void *src, 
		size_t spitch, 
		size_t width, 
		size_t height, 
		enum cudaMemcpyKind kind)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaMemcpy2DFromArray(void *dst, 
		size_t dpitch, 
		const struct cudaArray *src, 
		size_t wOffset, 
		size_t hOffset, 
		size_t width, 
		size_t height, 
		enum cudaMemcpyKind kind)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaMemcpy2DArrayToArray(struct cudaArray *dst, 
		size_t wOffsetDst, 
		size_t hOffsetDst, 
		const struct cudaArray *src, 
		size_t wOffsetSrc, 
		size_t hOffsetSrc, 
		size_t width, 
		size_t height, 
		enum cudaMemcpyKind kind __dv(cudaMemcpyDeviceToDevice))
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaMemcpyToSymbol(const void *symbol, 
		const void *src, 
		size_t count, 
		size_t offset __dv(0), 
		enum cudaMemcpyKind kind __dv(cudaMemcpyHostToDevice))
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaMemcpyFromSymbol(void *dst, 
		const void *symbol, 
		size_t count, 
		size_t offset __dv(0), 
		enum cudaMemcpyKind kind __dv(cudaMemcpyDeviceToHost))
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaMemcpyAsync(void *dst, 
		const void *src, 
		size_t count, 
		enum cudaMemcpyKind kind, 
		cudaStream_t stream __dv(0))
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaMemcpyPeerAsync(void *dst, 
		int dstDevice, 
		const void *src, 
		int srcDevice, 
		size_t count, 
		cudaStream_t stream __dv(0))
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaMemcpyToArrayAsync(struct cudaArray *dst, 
		size_t wOffset, 
		size_t hOffset, 
		const void *src, 
		size_t count, 
		enum cudaMemcpyKind kind, 
		cudaStream_t stream __dv(0))
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaMemcpyFromArrayAsync(void *dst, 
		const struct cudaArray *src, 
		size_t wOffset, 
		size_t hOffset, 
		size_t count, 
		enum cudaMemcpyKind kind, 
		cudaStream_t stream __dv(0))
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaMemcpy2DAsync(void *dst, 
		size_t dpitch, 
		const void *src, 
		size_t spitch, 
		size_t width, 
		size_t height, 
		enum cudaMemcpyKind kind, 
		cudaStream_t stream __dv(0))
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaMemcpy2DToArrayAsync(struct cudaArray *dst, 
		size_t wOffset, 
		size_t hOffset, 
		const void *src, 
		size_t spitch, 
		size_t width, 
		size_t height, 
		enum cudaMemcpyKind kind, 
		cudaStream_t stream __dv(0))
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaMemcpy2DFromArrayAsync(void *dst, 
		size_t dpitch, 
		const struct cudaArray *src, 
		size_t wOffset, 
		size_t hOffset, 
		size_t width, 
		size_t height, 
		enum cudaMemcpyKind kind, 
		cudaStream_t stream __dv(0))
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaMemcpyToSymbolAsync(const void *symbol, 
		const void *src, 
		size_t count, 
		size_t offset, 
		enum cudaMemcpyKind kind, 
		cudaStream_t stream __dv(0))
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaMemcpyFromSymbolAsync(void *dst, const void *symbol, size_t count, size_t offset, enum cudaMemcpyKind kind, cudaStream_t stream __dv(0))
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaMemset(void *devPtr, int value, size_t count)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaMemset2D(void *devPtr, 
		size_t pitch, 
		int value, 
		size_t width, 
		size_t height)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaMemset3D(struct cudaPitchedPtr pitchedDevPtr, 
		int value, 
		struct cudaExtent extent)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaMemsetAsync(void *devPtr, 
		int value, 
		size_t count, 
		cudaStream_t stream __dv(0))
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaMemset2DAsync(void *devPtr, 
		size_t pitch, 
		int value, 
		size_t width, 
		size_t height, 
		cudaStream_t stream __dv(0))
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaMemset3DAsync(struct cudaPitchedPtr pitchedDevPtr, 
		int value, 
		struct cudaExtent extent, 
		cudaStream_t stream __dv(0))
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaGetSymbolAddress(void **devPtr, const void *symbol)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaGetSymbolSize(size_t *size, const void *symbol)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaPointerGetAttributes(struct cudaPointerAttributes *attributes, 
		const void *ptr)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaDeviceCanAccessPeer(int *canAccessPeer, 
		int device, 
		int peerDevice)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaDeviceEnablePeerAccess(int peerDevice, unsigned int flags)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaDeviceDisablePeerAccess(int peerDevice)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaGraphicsUnregisterResource(cudaGraphicsResource_t resource)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaGraphicsResourceSetMapFlags(cudaGraphicsResource_t resource, 
		unsigned int flags)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaGraphicsMapResources(int count, 
		cudaGraphicsResource_t *resources, 
		cudaStream_t stream __dv(0))
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaGraphicsUnmapResources(int count, 
		cudaGraphicsResource_t *resources, 
		cudaStream_t stream __dv(0))
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaGraphicsResourceGetMappedPointer(void **devPtr, 
		size_t *size, 
		cudaGraphicsResource_t resource)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaGraphicsSubResourceGetMappedArray(struct cudaArray **array, 
		cudaGraphicsResource_t resource, 
		unsigned int arrayIndex, 
		unsigned int mipLevel)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaGetChannelDesc(struct cudaChannelFormatDesc *desc, 
		const struct cudaArray *array)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}


struct cudaChannelFormatDesc cudaCreateChannelDesc(int x, 
		int y, 
		int z, 
		int w, 
		enum cudaChannelFormatKind f)
{
	__CUDART_NOT_IMPL__;
	struct cudaChannelFormatDesc cfd;
	return cfd;
}

cudaError_t cudaBindTexture(size_t *offset, 
		const struct textureReference *texref, 
		const void *devPtr, 
		const struct cudaChannelFormatDesc *desc, 
		size_t size __dv(UINT_MAX))
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaBindTexture2D(size_t *offset, 
		const struct textureReference *texref, 
		const void *devPtr, 
		const struct cudaChannelFormatDesc *desc, 
		size_t width, 
		size_t height, 
		size_t pitch)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaBindTextureToArray(const struct textureReference *texref, 
		const struct cudaArray *array, 
		const struct cudaChannelFormatDesc *desc)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaUnbindTexture(const struct textureReference *texref)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaGetTextureAlignmentOffset(size_t *offset, 
		const struct textureReference *texref)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaGetTextureReference(const struct textureReference **texref, 
		const void *symbol)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaBindSurfaceToArray(const struct surfaceReference *surfref, 
		const struct cudaArray *array, 
		const struct cudaChannelFormatDesc *desc)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaGetSurfaceReference(const struct surfaceReference **surfref, 
		const void *symbol)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaDriverGetVersion(int *driverVersion)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaRuntimeGetVersion(int *runtimeVersion)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

cudaError_t cudaGetExportTable(const void **ppExportTable, 
		const cudaUUID_t *pExportTableId)
{
	__CUDART_NOT_IMPL__;
	return cudaSuccess;
}

